package simel;

import java.util.*;
import java.io.*;

public class DudeMarginAgent implements Agent
{
    Simulator sim;
    boolean directions[];
    boolean eitherDir[];
    boolean initialMoveFinished[];
    ArrayList<Passenger> upDudesList;
    ArrayList<Passenger> downDudesList;
    ArrayList<ArrayList<Elevator>> eleByGroup;
    ArrayList<ArrayList<Passenger>> eleDudes;
    ArrayList<Passenger> uncalledDudes;
    int eleGroups[];
    int upDudes[];
    int downDudes[];
    float dudeHurr[];
    int startingFloor[];
    int dudeAccum[];

    /** Note: Agents must have an empty constructor! **/
    public DudeMarginAgent()
    {
    }

    /** init() will be called before any other method **/
    public void init(Simulator sim)
    {

        this.sim = sim;

        directions = new boolean[sim.getElevators().size()];
        eitherDir = new boolean[sim.getElevators().size()];
        initialMoveFinished = new boolean[sim.getElevators().size()];
        //floorFreqs = new int[sim.floorMax];

        for(int i=0; i < sim.getElevators().size(); i++){
			eitherDir[i] = false;
		}

        Random r = new Random(0);

        // send each elevator to a random floor to start with.
        for (int i = 0; i < sim.getElevators().size(); i++) {
            Elevator e = sim.getElevators().get(i);
            e.setDestination(e.getFloorMin() + r.nextInt(e.getFloorMax() - e.getFloorMin()));
        	//e.setDestination(e.getFloor());
        }

		eleByGroup = new ArrayList<ArrayList<Elevator>>();
		eleGroups = new int[sim.getElevators().size()];
        Elevator e = sim.getElevators().get(0);
        eleGroups[0] = 0;
        int newGroup = 1;
        ArrayList<Elevator> newList = new ArrayList<Elevator>();
        newList.add(e);
        eleByGroup.add(newList);
        boolean foundGroup;
        for (int i = 1; i < sim.getElevators().size(); i++) {
			Elevator ee = sim.getElevators().get(i);
			foundGroup = false;
			for (int j = 0; j < i; j++) {
				Elevator c = sim.getElevators().get(j);
				if(ee.getFloorMax() == c.getFloorMax() && ee.getFloorMin() == c.getFloorMin()){
					ArrayList<Elevator> al = eleByGroup.get(eleGroups[j]);
					al.add(ee);
					eleByGroup.set(eleGroups[j], al);
					eleGroups[i] = eleGroups[j];
					j = i;
					foundGroup = true;
				}
			}
			if(!foundGroup){
				newList = new ArrayList<Elevator>();
				newList.add(ee);
				eleByGroup.add(newList);
				eleGroups[i] = newGroup;
				newGroup++;
			}
        }

        upDudes = new int[sim.floorMax];
        downDudes = new int[sim.floorMax];
        dudeHurr = new float[sim.floorMax];
        startingFloor = new int[sim.getElevators().size()];
        dudeAccum = new int[sim.floorMax];

		uncalledDudes = new ArrayList<Passenger>();
    }

    public void update(Simulator sim, ArrayList<Passenger> passengers, ArrayList<Elevator> elevators)
    {
		/*
		for (int j = 0; j < passengers.size(); j++) {
				Passenger p = passengers.get(j);
				if(p.elevator == -1) // waiting
					floorFreqs[p.floor-1]++;
		} */
		for(int i=0; i<sim.floorMax; i++){
			upDudes[i] = 0;
			downDudes[i] = 0;
		}

		uncalledDudes.clear();

		for(int i=0; i<passengers.size(); i++){
			Passenger p = passengers.get(i);
			uncalledDudes.add(p);
			if(p.elevator == -1){
				dudeAccum[p.getFloor()-1]++;
				if(p.getFloor() > p.getDestination())
					downDudes[p.getFloor()-1]++;
				else
					upDudes[p.getFloor()-1]++;
			}
		}

        for (int i = 0; i < elevators.size(); i++) {
            Elevator e = elevators.get(i);

            // If the elevator still hasn't arrived at its initial starting place, just wait.
            if (!initialMoveFinished[i]) {
                if (e.getFloor() == e.getDestination()) {
                    initialMoveFinished[i] = true;
                    // we've arrived. fall through to usual logic below...
                } else {
                    // keep waiting
                    continue;
                }
            }
		}

//System.out.println(eleByGroup);

			// call dudes
			int elevatourIndex = -1;
			for(int k=0; k<eleByGroup.size(); k++){
				for(int i=0; i < eleByGroup.get(k).size(); i++){
					elevatourIndex++;
					Elevator e = eleByGroup.get(k).get(i);
					if(directions[elevatourIndex]){ // up uguu
						for(int ii=uncalledDudes.size()-1; ii >= 0; ii--){
							Passenger p = uncalledDudes.get(ii);
							if(p.elevator != -1)
								uncalledDudes.remove(ii);
							else if(p.getFloor() <= (startingFloor[elevatourIndex]*2 + e.getDestination())/3 && p.getFloor() > (startingFloor[elevatourIndex] + 2*e.getDestination())/3 && p.getFloor() > p.getDestination())
								uncalledDudes.remove(ii);
						}
					} else {
						for(int ii=uncalledDudes.size()-1; ii >= 0; ii--){
							Passenger p = uncalledDudes.get(ii);
							if(p.elevator != -1)
								uncalledDudes.remove(ii);
							else if(p.getFloor() >= (startingFloor[elevatourIndex]*2 + e.getDestination())/3 && p.getFloor() < (startingFloor[elevatourIndex] + 2*e.getDestination())/3 && p.getFloor() < p.getDestination())
								uncalledDudes.remove(ii);
						}
					}
				}
			}


            // DudeMargin code
			int currentDudes, projectedDudes;
			float eta = 0.125f;
			elevatourIndex = -1;
			for(int k=0; k<eleByGroup.size(); k++){
				for(int i=0; i < eleByGroup.get(k).size(); i++){
					elevatourIndex++;
					Elevator e = eleByGroup.get(k).get(i);
					if(i == eleByGroup.get(k).size()-1){
						// dumb code
//System.out.println(elevatourIndex);
						if (e.getFloor() == e.getFloorMin())
							directions[elevatourIndex] = true;
						if (e.getFloor() == e.getFloorMax())
                			directions[elevatourIndex] = false;

                		if (directions[elevatourIndex])
						    e.setDestination(e.getFloor() + 1);
						else
            			    e.setDestination(e.getFloor() - 1);

					} else {
					if(e.getDestination() == e.getFloor()){ // if we're at dest
						if(eitherDir[elevatourIndex])
							directions[elevatourIndex] = !directions[elevatourIndex];
						startingFloor[elevatourIndex] = e.getFloor();
						for(int jk=0; jk<sim.floorMax; jk++){
							upDudes[jk] = 0;
							downDudes[jk] = 0;
						}

						currentDudes = 0;
						// dudes including on your elevator
						for(int eye=0; eye<uncalledDudes.size(); eye++){
							Passenger p = uncalledDudes.get(eye);
							if(p.elevator == -1){
								if(p.getFloor() > p.getDestination())
									downDudes[p.getFloor()-1] += sim.getTime() - p.getTimeBorn();
								else
									upDudes[p.getFloor()-1] += sim.getTime() - p.getTimeBorn();
							} else if(p.elevator == i){ // if dude is in elevator
								currentDudes++;
								if(directions[elevatourIndex]) // and we're going up
									downDudes[p.getDestination()-1] += sim.getTime() - p.getTimeBorn(); // add dudedestination to targets
								else // note that former updudes in an up elevator are now downdudes... hopefully it works
									upDudes[p.getDestination()-1] += sim.getTime() - p.getTimeBorn();
							}
						}

						projectedDudes = 0; // people we theoretically add
						if(directions[elevatourIndex]){ // up
							float maxDudeHurr = -100;
							int maxDudeFloor = 0;
							for(int f=e.getFloor(); f <= e.getFloorMax(); f++){
								dudeHurr[f-1] = downDudes[f-1] - eta*projectedDudes;
								projectedDudes += downDudes[f-1];
								if(dudeHurr[f-1] > maxDudeHurr){
									maxDudeHurr = dudeHurr[f-1];
									maxDudeFloor = f;
								}
							}
							if(maxDudeFloor == e.getFloor()){
								// see if there's anybody below either
								maxDudeHurr = -100;
								maxDudeFloor = 0;
								projectedDudes = 0;
								for(int f=e.getFloor(); f >= e.getFloorMin(); f--){
									dudeHurr[f-1] = downDudes[f-1] - eta*projectedDudes;
									projectedDudes += downDudes[f-1];
									if(dudeHurr[f-1] > maxDudeHurr){
										maxDudeHurr = dudeHurr[f-1];
										maxDudeFloor = f;
									}
								}
								if(maxDudeFloor == e.getFloor()){
									maxDudeHurr = 0;
									for(int dk = e.getFloorMin(); dk <= e.getFloorMax(); dk++){
										if(dudeAccum[dk-1] > maxDudeHurr){
											int dupe = 0;
											for(int pp=0; pp < eleByGroup.get(k).size(); pp++){
												Elevator pizza = sim.getElevators().get(pp);
												if(pizza.getFloor() == dk)
													dupe++;
											}
											if(dupe > eleByGroup.get(k).size()/6) {
												e.setDestination(dk);
												maxDudeHurr = dudeAccum[dk-1];
												eitherDir[elevatourIndex] = true;
											}
										}
									}
								} else {
									directions[elevatourIndex] = false;
									e.setDestination(e.getFloor() - 1);
									eitherDir[elevatourIndex] = false;
								}
							} else {
								e.setDestination(maxDudeFloor);
								eitherDir[elevatourIndex] = false;
								for(int ii=uncalledDudes.size()-1; ii >= 0; ii--){
									Passenger p = uncalledDudes.get(ii);
									if(p.elevator != -1)
										uncalledDudes.remove(ii);
									else if(p.getFloor() >= e.getDestination() && p.getFloor() < (startingFloor[elevatourIndex] + 2*e.getDestination())/3 && p.getFloor() < p.getDestination())
										uncalledDudes.remove(ii);
								}
							}
						} else { // same but with different shit (wait what?)
							float maxDudeHurr = -100;
							int maxDudeFloor = 0;
							for(int f=e.getFloor(); f >= e.getFloorMin(); f--){
								dudeHurr[f-1] = upDudes[f-1] - eta*projectedDudes;
								projectedDudes += upDudes[f-1];
								if(dudeHurr[f-1] > maxDudeHurr){
									maxDudeHurr = dudeHurr[f-1];
									maxDudeFloor = f;
								}
							}
							if(maxDudeFloor == e.getFloor()){
								// see if there's anybody below either
								maxDudeHurr = -100;
								maxDudeFloor = 0;
								projectedDudes = 0;
								for(int f=e.getFloor(); f <= e.getFloorMax(); f++){
									dudeHurr[f-1] = downDudes[f-1] - eta*projectedDudes;
									projectedDudes += downDudes[f-1];
									if(dudeHurr[f-1] > maxDudeHurr){
										maxDudeHurr = dudeHurr[f-1];
										maxDudeFloor = f;
									}
								}
								if(maxDudeFloor == e.getFloor()){
									maxDudeHurr = 0;
									for(int dk = e.getFloorMin(); dk <= e.getFloorMax(); dk++){
										if(dudeAccum[dk-1] > maxDudeHurr){
											int dupe = 0;
											for(int pp=0; pp < eleByGroup.get(k).size(); pp++){
												Elevator pizza = sim.getElevators().get(pp);
												if(pizza.getFloor() == dk)
													dupe++;
											}
											if(dupe > eleByGroup.get(k).size()/6) {
												e.setDestination(dk);
												maxDudeHurr = dudeAccum[dk-1];
												eitherDir[elevatourIndex] = true;
											}
										}
									}
								} else {
									directions[elevatourIndex] = true;
									e.setDestination(e.getFloor() + 1);
									eitherDir[elevatourIndex] = false;
								}
							} else {
								e.setDestination(maxDudeFloor);
								eitherDir[elevatourIndex] = false;
								for(int ii=uncalledDudes.size()-1; ii >= 0; ii--){
									Passenger p = uncalledDudes.get(ii);
									if(p.elevator != -1)
										uncalledDudes.remove(ii);
									else if(p.getFloor() >= e.getDestination() && p.getFloor() < (startingFloor[elevatourIndex] + 2*e.getDestination())/3 && p.getFloor() < p.getDestination())
										uncalledDudes.remove(ii);
								}
							}
						}
					} // if we're not at destination
					}
				}
			} // for k (not fork)



			// dumb code
			//if (e.getFloor() == e.getFloorMin())
            //    directions[i] = true;

            //if (e.getFloor() == e.getFloorMax())
            //    directions[i] = false;

            //if (directions[i])
            //    e.setDestination(e.getFloor() + 1);
            //else
            //    e.setDestination(e.getFloor() - 1);

    }
}
